/*
 * TP0 Organizacion de Computadoras 66.20
 * Practica: Santi - Perez Masci
 * Integrantes:
 * 				Pereira, Fernando Roque 91610
 *				Montoya, Diego Ramiro 91939
 *				Aguila Cudicio, Barbara 92071
 *
 */

#define ERROR_COMANDOS 1
#define ERROR_INPUT_FILE_NOT_CREATED 2
#define ERROR_OUTPUT_FILE_NOT_CREATED 3
#define ERROR_CARACTER_INVALIDO 4
#define MSG_HELP (" Usage:	tp0 -h
				tp0 -V
				tp0 [options]
				Options:
					-h, --help 	Print this information.
					-V, --version 	Print version and quit.
					-O, --octal 	Octal dump mode: encode the input (binary file) in octal format (default).
					-b, --binary 	Binary mode: decode the input (octal dump file) in binary format.
					-i, --input 	Path to input file (defaults to stdin).
					-o, --output 	Path to output file (defaults to stdout)).
					-l, --length 	Number of octal bytes per line (line length) at the output in octal dump mode (0 = default = no limit).
					-d, --delimiter Delimiter between octal bytes. Could be a string, not only a single byte. Used as delimiter for octal output and input files 					(default: none)")

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <math.h>

int codificar(FILE* inputFile, FILE* outputFile, int length, char* delim);
int decodificar(FILE* inputFile, FILE* outputFile, int length, char* delim);

int main (int argc, char* argv[]){

	int max_bytes = 0, c = 0;
	int octal = 1; //Flag para ver el modo de encoding
	char* delim = "";

	//Estructura que se pasa por parámetro a función 'getopt_long'
	static struct option long_commands[] = {
	                   {"help",0,NULL,'h'},
	                   {"version",0,NULL,'V'},
	                   {"octal",0, NULL,'O'},
	                   {"binary",0,NULL,'b'},
	                   {"input",1,NULL,'i'},
	                   {"output",1,NULL,'o'},
	                   {"length",1,NULL,'l'},
	                   {"delimiter",1,NULL,'d'},
	                   {0,0,0,0}
	               };

	opterr = 0; //Se indica 0 para que no imprima directamente mensaje de error

	FILE* inputFile = stdin;
	FILE* outputFile = stdout;

	/*	getopt_long: 	Devuelve los parametros en el int c. Si no hay mas parametros,
	 * 					devuelve -1.
	 * 		char* optarg - Nos da el argumento a analizar.
	*/
	while (((c = getopt_long(argc,argv,"hVObi:o:l:d:",long_commands,NULL)) != -1)){
		switch (c){
			case 'h': 	printf(MSG_HELP);
						return 0;
						break;

			case 'V': 	printf("\nVersión 1.0 \n\n");
						return 0;
						break;

			case 'O': 	octal = 1;
						break;

			case 'b': 	octal = 0;
						break;

			case 'i': 	if (strcmp(optarg,"-") != 0){
							inputFile = fopen(optarg,"rb");
							if (inputFile == NULL){
								fprintf(stderr, "Archivo de entrada no generado \n\n");
								return (ERROR_INPUT_FILE_NOT_CREATED);
							}
						}
						break;

			case 'o': 	if (strcmp(optarg,"-") != 0){
							outputFile = fopen(optarg,"wb");
							if (outputFile == NULL){
								fprintf(stderr, "Archivo de salida no generado \n\n");
								return ERROR_OUTPUT_FILE_NOT_CREATED;
							}
						}
						break;

			case 'l': 	max_bytes = atoi(optarg); //Indica cantidad maxima de bytes a imprimir
						break;

			case 'd': 	delim = (char*) malloc (strlen(optarg) +1);
						strcpy(delim,optarg); //Indica delimitador a utilizar
						break;

			case '?': 	fprintf(stderr,"Parámetro mal escrito o sin argumento necesario. "
										"Utilice -h para ver modo de uso y opciones");
						return ERROR_COMANDOS;
						break;

			default:	break;
		}
	}

	if (octal) codificar(inputFile, outputFile, max_bytes, delim);
	else decodificar(inputFile, outputFile, max_bytes, delim);

	if (strcmp(delim,"") != 0) free(delim);

	fclose(inputFile);
	fclose(outputFile);

	return 0;
}

int codificar(FILE* inputFile, FILE* outputFile, int max_bytes, char* delim){
	int c,counter = 0,cant_bytes = 3;

	//Impresion a octal
	while (!(feof(inputFile))){
		c = fgetc(inputFile);
		if (c != EOF){ //Comprobacion para no imprimir el caracter de fin de archivo
			fprintf(outputFile,"%.*o%s",cant_bytes,c,delim);
			counter = counter + 1;
			if (max_bytes == counter){
				fprintf(outputFile,"\n");
				counter = 0;
			}
		}
	}
	return 0;
}

/*Funcion para pasar un entero en base octal pasado por parametro a base decimal*/
int octalADecimal(int octal)
{
	int valor = 0, digito, decimal=0;
	while (octal > 0)
	{
		digito = octal%10;
		octal /= 10;
		decimal += digito * (int) pow (8,valor);
		valor++;
	}
	return decimal;
}

int decodificar(FILE* inputFile, FILE* outputFile, int max_bytes, char* delim){
	int i, limite = 3;
	char oct[3];
	int num, dec;
	while (!(feof(inputFile))){
		for (i=0; i < limite; i++){
			num = fgetc(inputFile);
			if (num == '\n') break; //Para que no trate el caracter de salto de linea
			if (num != EOF && num!= '\n'){
				oct[i] = (char) num;
			} else return 0; //Para que no imprima dos veces el caracter final
		}

		if (num == '\n') continue; //Para que no trate el caracter de salto de linea
		num = atoi(oct);
		dec = octalADecimal(num);
		fprintf(outputFile, "%c", dec);
		//Debe leer el delimitador
		if (strcmp(delim,"") != 0)
			for (i = 0; i < strlen(delim); i++)
				num = fgetc(inputFile);
	}
	return 0;
}
